﻿// <copyright file="EventLog.cs" company="Microsoft Corporation">
// Copyright (c) 2009 Microsoft Corporation All Rights Reserved
// </copyright>
// <author>Michael S. Scherotter</author>
// <email>mischero@microsoft.com</email>
// <date>2009-10-17</date>
// <summary>EventLog class definition</summary>

namespace Microsoft.WebAnalytics
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics.CodeAnalysis;
    using System.IO;
    using System.IO.IsolatedStorage;
    using System.Runtime.Serialization;
    using System.Security.Cryptography;
    using System.Text;

    /// <summary>
    /// Isolated Storage event log
    /// </summary>
    /// <remarks>Since this source code can be easily reflected (inspected),
    /// this event log is not meant to be a secure repository for analytics data.
    /// Personally Identifiable Information (PII) should not be stored in the event
    /// log.</remarks>
    public class EventLog
    {
        #region Fields
        
        /// <summary>
        /// The Password SALT for encrypting the event log
        /// </summary>
        /// <remarks>Since this source code can be easily reflected 
        /// (inspected), this is not a secure password.</remarks>
        private const string Salt = "Microsoft.WebAnalytics.SAF.Salt";

        /// <summary>
        /// The password for encrypting the event log
        /// </summary>
        /// <remarks>Since this source code can be easily reflected 
        /// (inspected), this is not a secure password.</remarks>
        private const string Password = "Microsoft.WebAnalytics.SAF.EventLog2010!";

        /// <summary>
        /// The Data contract serializer
        /// </summary>
        private DataContractSerializer serializer;

        #endregion

        #region Constructors
        /// <summary>
        /// Initializes a new instance of the EventLog class.
        /// </summary>
        /// <param name="fileName">the filename for the event log</param>
        /// <param name="scope">the scope (application or site)</param>
        /// <exception cref="IsolatedStorageException">The group quota for the store is set to zero.
        /// -or-
        /// The store has been removed but cannot be recreated because a directory or file is being used by another process. 
        /// -or-
        /// Isolated storage is disabled.
        /// </exception>
        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Exceptions here should not crash the app.")]
        public EventLog(string fileName, Scope scope)
        {
            this.Scope = scope;

            this.serializer = new DataContractSerializer(typeof(AnalyticsEvents));

            this.FileName = fileName;

            try
            {
                switch (this.Scope)
                {
                    case Scope.Application:
                        this.Storage = IsolatedStorageFile.GetUserStoreForApplication();
                        break;

                    case Scope.Site:
#if SILVERLIGHT
                        this.Storage = IsolatedStorageFile.GetUserStoreForSite();
#else
                        this.Storage = IsolatedStorageFile.GetUserStoreForDomain();
#endif
                        break;
                }

                this.ReadEventLog();
            }
            catch (IsolatedStorageException ise)
            {
                System.Diagnostics.Debug.WriteLine(ise.Message);
            }
            catch (System.Exception se)
            {
                System.Diagnostics.Debug.WriteLine(se.Message);
            }

            if (this.Events == null)
            {
                this.Events = new AnalyticsEvents();
            }
        }

        #endregion

        #region Properties
        /// <summary>
        /// Gets the analytics events that have been cached
        /// </summary>
        public AnalyticsEvents Events { get; private set; }

        /// <summary>
        /// Gets the event log filename
        /// </summary>
        public string FileName { get; private set; }

        /// <summary>
        /// Gets or sets the scope
        /// </summary>
        private Scope Scope { get; set; }

        /// <summary>
        /// Gets or sets the storage file
        /// </summary>
        private IsolatedStorageFile Storage { get; set; }

        #endregion

        #region Methods
        /// <summary>
        /// Write the entire log to isloated storage
        /// </summary>
        /// <exception cref="IsolatedStorageException">If the application cannot write to Isolated storage.</exception>
        public void WriteLog()
        {
            if (this.Storage == null)
            {
                return;
            }

            using (var fileStream = new IsolatedStorageFileStream(this.FileName, FileMode.Create, this.Storage))
            {
                this.serializer.WriteObject(fileStream, this.Events);
            }

            ////using (var fileStream = this.Storage.OpenFile(this.FileName, FileMode.Create))
            ////{
            ////    this.serializer.WriteObject(fileStream, this.Events);

            ////    fileStream.Close();
            ////}
        }

        /// <summary>
        /// Lock the writer and write an entry
        /// </summary>
        /// <param name="logEvent">the entry to write</param>
        public void WriteEntry(AnalyticsEvent logEvent)
        {
            this.Events.Events.Add(logEvent);
        }

        /// <summary>
        /// Get the events and remove them from the log
        /// </summary>
        /// <returns>a collection of analytics events</returns>
        internal IEnumerable<AnalyticsEvent> GetEvents()
        {
            lock (this.Events)
            {
                AnalyticsEvent[] array = new AnalyticsEvent[this.Events.Events.Count];

                this.Events.Events.CopyTo(array, 0);

                this.Events.Events.Clear();

                this.WriteLog();

                return array;
            }
        }

        #endregion

        #region Implementation
        /// <summary>
        /// Read the event log from isolated storage
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Any error reading should be caught.")]
        private void ReadEventLog()
        {
#if SILVERLIGHT
            if (this.Storage.FileExists(this.FileName))
#else
            if (this.Storage.GetFileNames(this.FileName).Length == 1)
#endif
            {
                ////using (var fileStream = this.Storage.OpenFile(this.FileName, FileMode.Open))

                using (var fileStream = new IsolatedStorageFileStream(this.FileName, FileMode.Open, this.Storage))
                {
                    this.Events = this.serializer.ReadObject(fileStream) as AnalyticsEvents;
                }
            }
        }
        #endregion
    }
}
